import java.util.ArrayList;
import java.util.LinkedList;

/**
 * This is the player class that keeps the players stats (how much money,
 * resouces, mules, etc they have) along with vital information about the player
 * (race, name, etc.).
 * 
 * @author GT Five-O
 * @version v1.0b
 * 
 */
public class Player implements Comparable, java.io.Serializable {

    public int playerID;
    private String playerName;
    private String playerRace;
    private String playerColor;
    private double playerMoney;
    private int score;
    private PlayerTimer timer = new PlayerTimer();
    private int currentRound;
    private Long roundStart;

    private LinkedList<Food> foods = new LinkedList<Food>();
    private LinkedList<Energy> energies = new LinkedList<Energy>();
    private LinkedList<Ore> ores = new LinkedList<Ore>();
    private LinkedList<Property> properties = new LinkedList<Property>();
    private LinkedList<Mule> mules = new LinkedList<Mule>();

    /**
     * getter for all the properties available.
     * 
     * @return properties, a LinkedList of all properties on the game screen
     */
    public LinkedList<Property> getProperties() {
        return properties;
    }

    /**
     * constructor method for the player. Assigns appropriate values to the
     * variables based on player type.
     * 
     * @param name
     * @param race
     * @param color
     */
    public Player(String name, String race, String color, int ID, String level) {
        playerID = ID;
        playerName = name;
        playerRace = race;
        playerColor = color;
        if (playerRace.equals("Human"))
            playerMoney = GameConstants.RACE_HUMAN_MONEY;
        else if (playerRace.equals("Flapper"))
            playerMoney = GameConstants.RACE_FLAPPER_MONEY;
        else
            playerMoney = GameConstants.RACE_OTHERS_MONEY;
        int counter = 0;
        if(level == "Beginner"){
            while (counter != 8) {
                foods.add(new Food());
                counter++;
            }
            counter = 0;
            while (counter != 4) {
                energies.add(new Energy());
                counter++;
            }
        }
        else if(level == "Intermediate"){
            while (counter != 4) {
                foods.add(new Food());
                counter++;
            }
            counter = 0;
            while (counter != 2) {
                energies.add(new Energy());
                counter++;
            }
        }
        else{
            while (counter != 2) {
                foods.add(new Food());
                counter++;
            }
            counter = 0;
            while (counter != 2) {
                energies.add(new Energy());
                counter++;
            }
        }
    }

    /**
     * getter for money.
     * 
     * @return
     */
    public double getMoney() {
        return playerMoney;
    }

    /**
     * getter for the count of the mules in a string format
     * 
     * @return
     */
    public String getMuleCount() {
        return "" + mules.size();
    }

    /**
     * getter for player's name.
     * 
     * @return
     */
    public String getPlayerName() {
        return playerName;
    }

    /**
     * getter for player's color.
     * 
     * @return
     */
    public String getPlayerColor() {
        return playerColor;
    }

    /**
     * getter for player's race.
     * 
     * @return
     */
    public String getPlayerRace() {
        return playerRace;
    }

    /**
     * When a player buys a piece of land, it deducts the price of the land from
     * their account.
     * 
     * @param num
     */
    public void buyProperty(int num) {
        playerMoney -= num;
    }

    /**
     * returns a boolean that states if the property selected can be afforded by
     * the player.
     * 
     * @param num
     * @return
     */
    public boolean affordable(int num) {
        return playerMoney >= num;
    }

    /**
     * getter for the score
     * 
     * @return
     */
    public int getScore() {
        setScore();
        return score;
    }

    /**
     * sets the score based on the size of the resources and properties
     */
    public void setScore() {
        score = foods.size() + energies.size() + ores.size()
                + properties.size();
    }

    /**
     * This method removes the food's first value until it's original size is
     * half of what it used to be
     */
    public void removeHalfFood() {
        int num = foods.size();
        while (foods.size() != num / 2) {
            foods.removeFirst();
        }
    }

    /**
     * getter for the food.
     * 
     * @return
     */
    public int getFood() {
        return foods.size();
    }

    /**
     * getter for the ore
     * 
     * @return
     */
    public int getOre() {
        return ores.size();
    }

    /**
     * getter for the energy
     * 
     * @return
     */
    public int getEnergy() {
        return energies.size();
    }

    /**
     * getter for the time
     * 
     * @param req
     * @return
     */
    public Long getTime(int req) {
        return timer.getPlayerTime(getFood(), req);
    }

    /**
     * finds the time based on the default java method to get the current time
     * 
     * @return
     */
    public Long currentTime() {
        Long fin = System.currentTimeMillis();
        return fin;
    }

    /**
     * setter for the time. It calles the timer's setRoundTime method to set
     * this.
     * 
     * @param num
     */
    public void setTimer(Long num) {
        timer.setRoundTime(num);
    }

    /**
     * returns a boolean to check if the round is over or not.
     * 
     * @return
     */
    public boolean roundCheck() {
        return timer.checkRound();
    }

    /**
     * getter for how many rounds the player has
     * 
     * @return
     */
    public Long roundLimit() {
        return (Long) timer.getRoundLimit();
    }

    /**
     * setter for the rounds played in the game
     * 
     * @param num
     */
    public void setRound(int num) {
        timer.setRound(num);
        currentRound = num;
    }

    /**
     * getter for the current round.
     * 
     * @return
     */
    public int getRound() {
        return currentRound;
    }

    /**
     * sets the roundStart variable with the parameter in addition to setting
     * the same value to the PlayerTimer's storeInit method
     * 
     * @param num
     */
    public void storeInit(Long num) {
        timer.storeInit(num);
        roundStart = num;
    }

    /**
     * getter for what the roundStart variable holds.
     * 
     * @return
     */
    public Long getInit() {
        return roundStart;
    }

    /**
     * getter for how much time the player has left in pub.
     * 
     * @return
     */
    public Long getTimeLeft() {
        return timer.pubTime();
    }

    /**
     * modifies the player's money by adding an amount
     * 
     * @param num
     *            amount being added
     */
    public void addMoney(int num) {
        playerMoney += num;
    }

    /**
     * modifies the player's money by subtracting an amount
     * 
     * @param num
     *            amount being subtracted
     */
    public void buying(int num) {
        playerMoney -= num;
    }

    /**
     * checks to see whether a a location is owned by the player
     * 
     * @param location
     *            location being searched
     * @return true if the player owns the property
     */
    public boolean hasProp(String location) {
        for (Property prop : properties) {
            if (prop.getLocation().equals(location)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Adds property bought by player.
     * 
     * @param bought
     */
    public void addProp(Property bought) {
        properties.add(bought);
    }

    /**
     * Adds a food unit to the player
     * 
     * @param f
     *            food unit being added
     */
    public void addFood(Food f) {
        foods.add(f);
    }

    /**
     * subtracts a food element
     * 
     * @return the food that was removed
     */
    public Food removeFood() {
        if (foods.size() > 0) {
            Food f = foods.remove();
            return f;
        } else {
            return null;
        }

    }

    /**
     * Adds a energy unit to the player
     * 
     * @param e
     *            energy unit being added
     */
    public void addEnergy(Energy e) {
        energies.add(e);
    }

    /**
     * subtracts a energy unit
     * 
     * @return the energy unit that was removed
     */
    public Energy removeEnergy() {
        if (energies.size() > 0) {
            Energy e = energies.remove();
            return e;
        } else {
            return null;
        }

    }

    /**
     * Adds a ore unit to the player
     * 
     * @param o
     *            ore unit being added
     */
    public void addOre(Ore o) {
        ores.add(o);
    }

    /**
     * subtracts a ore unit
     * 
     * @return the ore unit that was removed
     */
    public Ore removeOre() {
        if (ores.size() > 0) {
            Ore o = ores.remove();
            return o;
        } else {
            return null;
        }
    }

    /**
     * adds a mule to the play's inventory
     * 
     * @param m
     *            the mule being added
     */
    public void addMule(Mule m) {
        mules.add(m);
    }

    /**
     * removes a mule from the players inventory
     * 
     * @param m
     *            the mule being removed
     */
    public void removeMule(Mule m) {
        if (mules.size() > 0) {
            mules.removeLast();
        }
    }

    /**
     * Our own implementation of the compareTo method, overriding the original,
     * used in the player class. A player is greater than the next if they have
     * a higher score (returning 1), if they have the same score, it returns 0,
     * if it is less, the method returns a -1.
     */
    @Override
    public int compareTo(Object arg0) {
        Player p = (Player) arg0;
        if (this.getScore() > p.getScore()) {
            return 1;
        } else if (this.getScore() == p.getScore()) {
            return 0;
        } else {
            return -1;
        }
    }

    /**
     * checks if the user has any property at all
     * 
     * @return false if the user has no property
     */
    public boolean hasProperty() {
        return properties.size() != 0;
    }

    // This method needs to be filled in to called the harvest method on each
    // mule the player owns
    /**
     * has all the mules add their harvest to the player's inventory. Energy
     * mules will yield energy units, Ore mules will yield ore units, and Food
     * mules will yield food units
     */
    public void updateResources() {
        for (Mule m : mules) {
            if (energies.size() > 0) {
                removeEnergy();
                if (m.getType().equals("Energy")) {
                    LinkedList<Resource> result = m.harvest(m.getProperty()
                            .getEnergyInfo());
                    LinkedList<Energy> result2 = new LinkedList();
                    for (Resource i : result) {
                        result2.add((Energy) i);
                    }
                    energies.addAll(result2);
                } else if (m.getType().equals("Ore")) {
                    LinkedList<Resource> result = m.harvest(m.getProperty()
                            .getOreInfo());
                    LinkedList<Ore> result2 = new LinkedList();
                    for (Resource i : result) {
                        result2.add((Ore) i);
                    }
                    ores.addAll(result2);
                } else {
                    LinkedList<Resource> result = m.harvest(m.getProperty()
                            .getFoodInfo());
                    LinkedList<Food> result2 = new LinkedList();
                    for (Resource i : result) {
                        result2.add((Food) i);
                    }
                    foods.addAll(result2);
                }
            }
        }
    }

}
